/*
* $Id: PointManager.java 278 2011-03-04 22:31:44Z volok-aleksej@yandex.ru $
*
* Copyright 2008-2011 by Volokitin Alexey
* All rights reserved
*
*/
package apiary.db.managers;

import java.util.Vector;
import java.util.Enumeration;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import apiary.db.PointStore;
import apiary.db.PointStore.PointRecord;
import apiary.ui.journal.PointForm.PointValue;
import apiary.ui.journal.UserFieldForm.UserField;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;

/**
 * Point database manager
 * @author lexus
 */
public class PointManager
{
    /** instance class */
    private static PointManager instance = null;
    /**
     * Gets instance class
     * @return instance class
     */
    protected static PointManager getInstance() {
        return instance;
    }

    /** point database */
    private PointStore pointsStore;
    /** maximal point identificator */
    private int maxIdPoint;
    /** list of points */
    private Vector dbPoints;

    /**
     * Constructor
     */
    public PointManager() {
        instance = this;
        pointsStore = new PointStore();
        dbPoints = new Vector();
        readPoints();
    }

    /**
     * Gets all points
     * @return list of points
     */
    protected Vector getPoints() {
        return dbPoints;
    }

    /**
     * Gets point on identificator
     * @param idPoint identificator of point
     * @return point
     */
    protected PointRecord getPoint(int idPoint) {
        for(int i = 0; i < dbPoints.size(); i++) {
            PointRecord point = (PointRecord) dbPoints.elementAt(i);
            if(point.idPoint == idPoint)
                return point;
        }
        return null;
    }

    /**
     * Adds point at list.
     * Data will be parsed and created new point object.
     * @param data binary data that describe point
     */
    protected void addPoint(byte[] data) {
        PointRecord pointRecord = (PointRecord) pointsStore.getRecord();
        int offset = pointRecord.setData(data);
        dbPoints.addElement(pointRecord);
        if(maxIdPoint < pointRecord.idPoint)
            maxIdPoint = pointRecord.idPoint;
        FieldsValueManager.getInstance().addFieldValues(data, offset);
    }

    /**
     * Adds point at list.
     * Converts point to database format and adds at list.
     * @param point point value
     * @return binary data that describe point
     */
    protected byte[] addPoint(PointValue point) {
        maxIdPoint = nextIdPoint();
        int idRecord = 0;
        if(point.userFields.size() > 0)
            idRecord = FieldsValueManager.getInstance().nextIdRecord();
        PointRecord pointRecord = (PointRecord) pointsStore.getRecord();
        pointRecord.idPoint = maxIdPoint;
        pointRecord.idFieldValue = idRecord;
        ConvertData.PointValueToRecord(point, pointRecord);
        dbPoints.addElement(pointRecord);
        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            outputStream.write(pointRecord.getData());
            for(int i = 0; i < point.userFields.size(); i++) {
                UserField field = (UserField) point.userFields.elementAt(i);
                byte[] fieldValue = FieldsValueManager.getInstance().addFieldValue(field, idRecord);
                if(fieldValue == null)
                    continue;
                outputStream.write(fieldValue);
            }
            return outputStream.toByteArray();
        } catch (IOException ex) {}
        return null;
    }

    /**
     * Changes point at list.
     * Data will be parsed and changed point object.
     * @param data binary data that describe point
     */
    protected void changePoint(byte[] data) {
        PointRecord pointRecord = (PointRecord) pointsStore.getRecord();
        pointRecord.setData(data);
        PointRecord point = getPoint(pointRecord.idPoint);
        if(point == null)
            return;
        int offset = point.setData(data);
        FieldsValueManager.getInstance().changeFieldValues(data, offset);
    }

    /**
     * Changes point at list.
     * Converts point to database format and changes at list.
     * @param id identificator point
     * @param point point value
     * @return binary data that describe point
     */
    protected byte[] changePoint(int id, PointValue point) {
        int idRecord = 0;
        PointRecord pointRecord = getPoint(id);
        if(pointRecord == null)
            return null;
        if(point.userFields.size() > 0 && pointRecord.idFieldValue == 0)
            idRecord = FieldsValueManager.getInstance().nextIdRecord();
        else if(point.userFields.size() > 0 && pointRecord.idFieldValue != 0)
            idRecord = pointRecord.idFieldValue;
        pointRecord.idFieldValue = idRecord;
        ConvertData.PointValueToRecord(point, pointRecord);
        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            outputStream.write(pointRecord.getData());
            for(int i = 0; i < point.userFields.size(); i++) {
                UserField field = (UserField) point.userFields.elementAt(i);
                byte[] fieldValue = FieldsValueManager.getInstance().changeFieldValue(field, idRecord);
                if(fieldValue == null)
                    continue;
                outputStream.write(fieldValue);
            }
            return outputStream.toByteArray();
        } catch (IOException ex) {}
        return null;
    }

    /**
     * Delete point from list.
     * @param data binary data that describe point
     */
    protected void deletePoint(byte[] data) {
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(data);
            DataInputStream dis = new DataInputStream(bais);
            deletePointFromList(dis.readInt());
        } catch (IOException ex) {}
    }

    /**
     * Delete point from list.
     * @param id identificator point
     */
    protected byte[] deletePoint(int id) {
        try {
            deletePointFromList(id);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            DataOutputStream dos = new DataOutputStream(baos);
            dos.writeInt(id);
            return baos.toByteArray();
        } catch (IOException ex) {}
        return null;
    }

    /**
     * Calculates next point identificator
     * @return identificator
     */
    protected int nextIdPoint() {
        return maxIdPoint + 1;
    }

    /**
     * Resets all data in manager
     */
    protected void resetManager() {
        dbPoints.removeAllElements();
        maxIdPoint = 0;
        readPoints();
    }

    /**
     * Reads point database
     */
    private void readPoints() {
        Enumeration pointsEnum = pointsStore.getRecordsEnumeration();
        do {
            if(!pointsEnum.hasMoreElements())
                break;
            PointRecord record = (PointRecord) pointsEnum.nextElement();
            dbPoints.addElement(record);
            if(maxIdPoint < record.idPoint)
                maxIdPoint = record.idPoint;
        } while(true);
    }

    /**
     * Delete point from list.
     * @param id identificator point
     */
    private void deletePointFromList(int id) {
        for(int i = 0; i < dbPoints.size(); i++) {
            PointRecord pointRecord = (PointRecord) dbPoints.elementAt(i);
            if(pointRecord.idPoint == id) {
                FieldsValueManager flmManager = FieldsValueManager.getInstance();
                flmManager.deleteValues(pointRecord.idFieldValue);
                FamilyManager familyManager = FamilyManager.getInstance();
                familyManager.deleteFamilies(pointRecord.idPoint);
                dbPoints.removeElementAt(i);
                break;
            }
        }
    }
}
